package ldh.im.handle;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import ldh.im.ClientChannelService;
import ldh.im.ImCoder;
import ldh.im.ImConfig;
import ldh.im.dto.DtoData;
import ldh.im.dto.Header;
import ldh.im.TransferData;
import ldh.im.dto.base.Dto;
import ldh.im.dto.codec.BaseImCoder;
import ldh.im.dto.codec.CodecFactory;
import ldh.im.dto.codec.binery.TransferCode;
import ldh.im.util.ByteBufHelp;
//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;

import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TransferDataCoderHandler extends ByteToMessageCodec<TransferData> {

//    private static final Logger LOGGER = LoggerFactory.getLogger(TransferDataCoderHandler.class);
    protected static final Charset utf8 = Charset.forName("utf8");
    private ClientChannelService clientChannelService;

    public TransferDataCoderHandler() {}

    public TransferDataCoderHandler(ClientChannelService clientChannelService) {
        this.clientChannelService = clientChannelService;
    }

    @Override
    protected void encode(ChannelHandlerContext ctx, TransferData transferData, ByteBuf out) throws Exception {
        try {
            ByteBuf byteBuf = ctx.alloc().buffer();
            Header header = buildHeader(transferData);
            int headerLength = writeHeaders(byteBuf, header);

            int writeIndex = byteBuf.writerIndex();
            byteBuf.writeInt(0);  // 4字节
            int bodyLength = writeBody(byteBuf, transferData);
//            int length = headerLength + bodyLength + 4;
            byteBuf.setInt(writeIndex, bodyLength);

            out.writeBytes(byteBuf);
        } catch (Exception e) {
//            LOGGER.error("编码错误", e);
        }
    }

    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int total = 0;
        short coder = 0;
        try {
            while(in.readableBytes() > 0) {
                int readAbleLength = in.readableBytes();
                Header header = readerHeader(in);
//                coder = header.getCoder();
                int headLength = readAbleLength - in.readableBytes();
                if(in.readableBytes() > 0) {
                    total = in.readInt();
//                    LOGGER.debug(String.format("接受到数据， 长度：%s, 可读长度: %s", total, in.readableBytes()));

                    int bodyTotal = total; // - headLength - 2;
                    assert bodyTotal <= in.readableBytes();

                    handleData(in, header, bodyTotal, out);
                }
            }
        } catch (Exception e) {
            in.skipBytes(in.readableBytes());
//            LOGGER.error(String.format("解码错误, 类型:%s, 长度：%s", coder, total), e);
        }
    }

    protected Object handleData(ByteBuf in, Header header, int bodyTotal, List<Object> out) {
        short coder = in.readShort();
        TransferData transferData = new TransferData();
        TransferCode transferCode = CodecFactory.getInstance().getTransfer(coder);
        if(transferCode == null) {
            in.skipBytes(in.readableBytes());
            throw new RuntimeException(String.format("%s没有对应的编码器", coder));
        }
        Dto dto = transferCode.getImCoder().decode(in);
        transferData.setData(dto);
        transferData.setCoder(coder);
        out.add(transferData);
        return transferData;
    }

    // 构建表头
    protected Header buildHeader(TransferData transferData) {
        Header header = createHeader();
        TransferCode transferCode = CodecFactory.getInstance().getTransfer(transferData.getData().getClass(), transferData.getData().getCodecType());
        if (transferCode == null) {
            String errorMsg = String.format("%s没有编码器", transferData.getData().getClass());
//            LOGGER.error(errorMsg);
            throw new RuntimeException(errorMsg);
        }
        short voCode = transferCode.getCode();
        ImCoder imCoder = transferCode.getImCoder();
        int loginCode = imCoder.getAuthCode();
        if (loginCode == 0 && clientChannelService != null && clientChannelService.isLogin()) {
            loginCode = clientChannelService.getAuthCode();
        }
//        header.setCoder(voCode);
        header.setAuthCode(loginCode);
//        header.setProperties(transferData.getHeaders());
        return header;
    }

    // 构建表头
    protected Header createHeader() {
        return new Header();
    }

    protected Header readerHeader(ByteBuf in) {
        Header header = createHeader();
        header.setVersion(in.readByte());
        header.setAuthCode(in.readInt());
//        header.setCoder(in.readShort());

//        header.setProperties(readHeaderProperties(in));
        return header;
    }

    protected Map<String, String> readHeaderProperties(ByteBuf in) {
        int propertyLength = in.readShort();
        Map<String, String> properties = new HashMap<>();
        for(int i=0; i<propertyLength; i++) {
            String key = ByteBufHelp.readString(in, utf8);
            String value = ByteBufHelp.readString(in, utf8);
            properties.put(key, value);
        }
        return properties;
    }

    // 客户端头部最少9字节
    protected int writeHeaders(ByteBuf byteBuf, Header header) {
        byteBuf.writeByte(header.getVersion());
        byteBuf.writeInt(header.getAuthCode());
//        byteBuf.writeShort(header.getCoder());

//        int length = writeHeaderProperties(byteBuf, header.getProperties()) + 7;
        int length = 5;
        return length;
    }

    protected int writeHeaderProperties(ByteBuf byteBuf, Map<String, String> headers) {
        int length = 0;
        if (headers == null) {
            byteBuf.writeShort(0);
            length += 2;
        } else {
            byteBuf.writeShort(headers.size());
            for(Map.Entry<String, String> entry : headers.entrySet()) {
                int lg = ByteBufHelp.writeString(byteBuf, entry.getKey(), utf8);
                int vlg = ByteBufHelp.writeString(byteBuf, entry.getValue(), utf8);
                length += (lg + vlg);
            }
        }
        return length;
    }

    protected int writeBody(ByteBuf byteBuf, TransferData transferData) {
        TransferCode transferCode = CodecFactory.getInstance().getTransfer(transferData.getData().getClass(), transferData.getData().getCodecType());
        byteBuf.writeShort(transferCode.getCode());
        Object encodeData = transferCode.getImCoder().encode(transferData.getData());
        int length = 2;
        if (encodeData instanceof ByteBuf) {
            ByteBuf dataByteBuf = (ByteBuf) encodeData;
            length = dataByteBuf.readableBytes();
            byteBuf.writeBytes(dataByteBuf);
        } else if (encodeData instanceof String) {
            String encodeStr = (String) encodeData;
            length = ByteBufHelp.writeString(byteBuf, encodeStr, BaseImCoder.utf8);
        }
        return length;
    }

}
